using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using SubversionReports.Entities;
using SubversionReports.Utilities;

namespace SubversionReports.Web.Controls
{
    public partial class RevisionsFilters : UserControl
    {
        /// <summary>
        /// Handles the Load event of the Page control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void Page_Load(object sender, EventArgs e)
        {
            if(!IsPostBack && SvnWebDav.AuthenticationSuceeded)
            {
                Filters filters = new Filters();
                Repository repository = SvnWebDav.GetRepositoryLog(SvnWebDav.CurrentRepositoryRootUrl, SvnWebDav.CurrentRepositoryUsername, SvnWebDav.CurrentRepositoryPassword);
                
                AuthorList.Items.Clear();
                AuthorList.Items.Add(string.Empty);
                foreach (KeyValuePair<string, string> icon in repository.AuthorIcons)
                {
                    ListItem listItem = new ListItem(icon.Key);
                    listItem.Selected = filters.ContainsKey("Author") && filters["Author"].MinValue.Equals(icon.Key);
                    AuthorList.Items.Add(listItem);
                }

                AuthorIncludeExclude.Items.Clear();
                DateIncludeExclude.Items.Clear();
                RevisionIncludeExclude.Items.Clear();
                foreach (KeyValuePair<int, string> filterType in EnumerationHelper.GetEnumDictionary(typeof(FilterType)))
                {
                    ListItem authorListItem = new ListItem(filterType.Value);
                    authorListItem.Selected = filters.ContainsKey("Author") && filters["Author"].FilterType.Equals(filterType.Value);
                    AuthorIncludeExclude.Items.Add(authorListItem);

                    ListItem dateListItem = new ListItem(filterType.Value);
                    dateListItem.Selected = filters.ContainsKey("Date") && filters["Date"].FilterType.Equals(filterType.Value);
                    DateIncludeExclude.Items.Add(dateListItem);

                    ListItem revisionListItem = new ListItem(filterType.Value);
                    revisionListItem.Selected = filters.ContainsKey("Revision") && filters["Revision"].FilterType.Equals(filterType.Value);
                    RevisionIncludeExclude.Items.Add(revisionListItem);
                }

                StringBuilder stringBuilder = new StringBuilder();
                if (filters.ContainsKey("Author"))
                    stringBuilder.AppendFormat("{0} author {1}", filters["Author"].FilterType, filters["Author"].MinValue);
                if (filters.ContainsKey("Revision"))
                    stringBuilder.AppendFormat("{0}{1} revisons {2} to {3}", !string.IsNullOrEmpty(stringBuilder.ToString()) ? ", " : string.Empty, filters["Revision"].FilterType, filters["Revision"].MinValue, filters["Revision"].MaxValue);
                if (filters.ContainsKey("Date"))
                    stringBuilder.AppendFormat("{0}{1} from {2} to {3}", !string.IsNullOrEmpty(stringBuilder.ToString()) ? ", " : string.Empty, filters["Date"].FilterType, filters["Date"].MinValue, filters["Date"].MaxValue);
                FilterLabel.Text = (stringBuilder.Length > 0) ? stringBuilder.ToString() : "All revisions";
            }
        }

        /// <summary>
        /// Handles the Click event of the ViewFilterButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void ViewFilterButton_Click(object sender, EventArgs e)
        {
            FilterSelectionsTable.Visible = (!FilterSelectionsTable.Visible);
            FilterLabel.Visible = (!FilterSelectionsTable.Visible);
            ViewFilterButton.ImageUrl = (FilterSelectionsTable.Visible) ? "~/Images/minus.png" : "~/Images/plus.png";
        }

        /// <summary>
        /// Handles the Click event of the ApplyFiltersButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void ApplyFiltersButton_Click(object sender, EventArgs e)
        {
            Filters filters = new Filters();

            if(!string.IsNullOrEmpty(AuthorList.SelectedValue))
                filters.Add(new Filter("Author", AuthorList.SelectedValue, AuthorIncludeExclude.SelectedValue));
            else filters.RemoveAt("Author");

            if (!string.IsNullOrEmpty(StartDate.Text) && !string.IsNullOrEmpty(EndDate.Text))
                filters.Add(new Filter("Date", StartDate.Text, EndDate.Text, DateIncludeExclude.SelectedValue));
            else filters.RemoveAt("Date");

            if (!string.IsNullOrEmpty(StartRevision.Text) && !string.IsNullOrEmpty(EndRevision.Text))
                filters.Add(new Filter("Revision", StartRevision.Text, EndRevision.Text, RevisionIncludeExclude.SelectedValue));
            else filters.RemoveAt("Revision");

            QueryStringParameters queryStringParameters = new QueryStringParameters();
            queryStringParameters.Add("Filters", filters.ToString());
            Response.Redirect(string.Format("{0}?{1}", Request.FilePath, queryStringParameters.ToString()), true);
        }

        /// <summary>
        /// Handles the Click event of the RemoveFiltersButton control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        protected void RemoveFiltersButton_Click(object sender, EventArgs e)
        {
            QueryStringParameters queryStringParameters = new QueryStringParameters();
            if (queryStringParameters.ContainsKey("Filters"))
                queryStringParameters["Filters"] = string.Empty;
            Response.Redirect(string.Format("{0}?{1}", Request.FilePath, queryStringParameters.ToString()), true);
        }

        /// <summary>
        /// Determines whether the given key exists in the query string.
        /// </summary>
        /// <param name="key">The key.</param>
        /// <returns></returns>
        protected static bool QueryStringContainsKey(string key)
        {
            foreach (string k in HttpContext.Current.Request.QueryString.AllKeys)
                if (k.Equals(key))
                    return true;
            return false;
        }

        internal class Filter
        {
            private string key;
            private string minValue;
            private string maxValue;
            private string filterType;

            /// <summary>
            /// Gets or sets the key.
            /// </summary>
            /// <value>The key.</value>
            public string Key
            {
                get { return key; }
                set { key = value; }
            }

            /// <summary>
            /// Gets or sets the min value.
            /// </summary>
            /// <value>The min value.</value>
            public string MinValue
            {
                get { return minValue; }
                set { minValue = value; }
            }

            /// <summary>
            /// Gets or sets the max value.
            /// </summary>
            /// <value>The max value.</value>
            public string MaxValue
            {
                get { return maxValue; }
                set { maxValue = value; }
            }

            /// <summary>
            /// Gets or sets the type of the filter.
            /// </summary>
            /// <value>The type of the filter.</value>
            public string FilterType
            {
                get { return filterType; }
                set { filterType = value; }
            }

            /// <summary>
            /// Initializes a new instance of the <see cref="Filter"/> class.
            /// </summary>
            /// <param name="key">The key.</param>
            /// <param name="value">The value.</param>
            /// <param name="filterType">Type of the filter.</param>
            public Filter(string key, string value, string filterType) : this(key, value, value, filterType) { }

            /// <summary>
            /// Initializes a new instance of the <see cref="Filter"/> class.
            /// </summary>
            /// <param name="key">The key.</param>
            /// <param name="minValue">The min value.</param>
            /// <param name="maxValue">The max value.</param>
            /// <param name="filterType">Type of the filter.</param>
            public Filter(string key, string minValue, string maxValue, string filterType)
            {
                this.key = key;
                this.minValue = minValue;
                this.maxValue = maxValue;
                this.filterType = filterType;
            }

            /// <summary>
            /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
            /// </summary>
            /// <returns>
            /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
            /// </returns>
            new public string ToString()
            {
                return (minValue.Equals(maxValue)) ? string.Format("{0}|{1}|{2}", key, minValue, filterType) : string.Format("{0}|{1}|{2}|{3}", key, minValue, maxValue, filterType);
            }

            /// <summary>
            /// Gets the filter parameters.
            /// </summary>
            /// <param name="filter">The filter.</param>
            /// <returns></returns>
            public static string[] GetFilterParameters(string filter)
            {
                return filter.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
            }

            public static Filter GetFilter(string filter)
            {
                string[] filterParameters = GetFilterParameters(filter);
                if (filterParameters.Length.Equals(3))
                    return new Filter(filterParameters[0], filterParameters[1], filterParameters[2]);
                else if (filterParameters.Length.Equals(4))
                    return new Filter(filterParameters[0], filterParameters[1], filterParameters[2], filterParameters[3]);

                throw new ArgumentException(filter);
            }
        }

        internal class Filters : List<Filter>
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="Filters"/> class.
            /// </summary>
            public Filters()
            {
                if(QueryStringContainsKey("Filters"))
                    foreach (string filter in HttpContext.Current.Request.QueryString["Filters"].Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
                        base.Add(Filter.GetFilter(filter));
            }

            /// <summary>
            /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
            /// </summary>
            /// <returns>
            /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
            /// </returns>
            new public string ToString()
            {
                List<string> filters = new List<string>();
                ForEach(delegate(Filter filter) { filters.Add(filter.ToString()); });
                return string.Join(",", filters.ToArray());
            }

            /// <summary>
            /// Adds the specified filter string.
            /// </summary>
            /// <param name="filter">The filter.</param>
            new public void Add(Filter filter)
            {
                RemoveAt(filter.Key);
                base.Add(filter);
            }

            /// <summary>
            /// Removes at.
            /// </summary>
            /// <param name="key">The key.</param>
            public void RemoveAt(string key)
            {
                int index = -1;
                int i = 0;
                ForEach(delegate(Filter delegateFilter) { if (delegateFilter.Key.Equals(key)) index = i; else i++; });
                if (index > -1)
                    RemoveAt(index);
            }

            /// <summary>
            /// Gets the <see cref="SubversionReports.Web.Controls.RevisionsFilters.Filter"/> with the specified key.
            /// </summary>
            /// <value></value>
            public Filter this[string key]
            {
                get
                {
                    int index = -1;
                    int i = 0;
                    ForEach(delegate(Filter filter) { if (filter.Key.Equals(key)) index = i; else i++; });
                    return (index > -1) ? base[index] : null;
                }
            }

            /// <summary>
            /// Determines whether the specified key contains key.
            /// </summary>
            /// <param name="key">The key.</param>
            /// <returns>
            /// 	<c>true</c> if the specified key contains key; otherwise, <c>false</c>.
            /// </returns>
            public bool ContainsKey(string key)
            {
                return (Find(delegate(Filter filter) { return filter.Key.Equals(key); }) != null);
            }
        }

        internal class QueryStringParameters : Dictionary<string, string>
        {
            /// <summary>
            /// Initializes a new instance of the <see cref="QueryStringParameters"/> class.
            /// </summary>
            public QueryStringParameters()
            {
                foreach (string key in HttpContext.Current.Request.QueryString.AllKeys)
                    Add(key, HttpContext.Current.Request.QueryString[key]);
            }

            /// <summary>
            /// Adds the specified key and value to the dictionary.
            /// </summary>
            /// <param name="key">The key of the element to add.</param>
            /// <param name="value">The value of the element to add. The value can be null for reference types.</param>
            /// <exception cref="T:System.ArgumentException">An element with the same key already exists in the <see cref="T:System.Collections.Generic.Dictionary`2"></see>.</exception>
            /// <exception cref="T:System.ArgumentNullException">key is null.</exception>
            new public void Add(string key, string value)
            {
                if(ContainsKey(key))
                    base[key] = value;
                else
                    base.Add(key, value);
            }

            /// <summary>
            /// Gets or sets the <see cref="System.String"/> with the specified key.
            /// </summary>
            /// <value></value>
            new public string this[string key]
            {
                set
                {
                    if(ContainsKey(key))
                        base[key] = value;
                    else
                        base.Add(key, value);
                }
                get { return base[key]; }
            }

            /// <summary>
            /// Returns a <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
            /// </summary>
            /// <returns>
            /// A <see cref="T:System.String"></see> that represents the current <see cref="T:System.Object"></see>.
            /// </returns>
            new public string ToString()
            {
                List<string> parameters = new List<string>();
                foreach (KeyValuePair<string, string> pair in this)
                    parameters.Add(string.Format("{0}={1}", pair.Key, pair.Value));
                return string.Join("&", parameters.ToArray());
            }
        }
    }
}